DESeq2-based analysis
I realized that motifs by samples is similar to genes by samples
GFP vs iPSC
dds$Type <- relevel(dds$Type, ref='RFP')
res_GFP_iPSC <- results(dds, contrast=c("Type","GFP","iPSC"))
res_GFP_RFP <- results(dds, contrast=c("Type","GFP","RFP"))
#plotMA(res_GFP_iPSC)
#plot(hist(res_GFP_iPSC$pvalue, breaks=1000))
# Load in RNA-seq data
gfp_rfp <- read_csv('~/git/ipsc_rpe_RNA-seq/data/GFP_vs_RFP.results.csv')
Parsed with column specification:
cols(
Gene = col_character(),
baseMean = col_double(),
log2FoldChange = col_double(),
lfcSE = col_double(),
stat = col_double(),
pvalue = col_double(),
padj = col_double()
)
rpe_ipsc <- read_csv('~/git/ipsc_rpe_RNA-seq/data/RPE_vs_iPSC.results.csv')
Parsed with column specification:
cols(
Gene = col_character(),
baseMean = col_double(),
log2FoldChange = col_double(),
lfcSE = col_double(),
stat = col_double(),
pvalue = col_double(),
padj = col_double()
)
#gfp_rfp %>% head()
Table of results
Motifs high in GFP
# calculate z scores for sample_bootstrap_counts
Z_scoring <- sample_bootstrap_counts %>%
# collapse by Type, motif, and whether bootstrap or real
group_by(sample, motif_alt_id) %>%
mutate(`Z score` = scale(Count)) %>%
filter(bootstrap == 'real') %>%
select(-bootstrap, -TF) %>%
left_join(tf_motif)
Joining, by = "motif_alt_id"
#Z_scoring %>% head()
The Z score is the number of standard deviation of motifs found in the sample peaks above the random set of peaks
results <- res_GFP_iPSC %>%
data.frame() %>%
rownames_to_column('motif_alt_id') %>%
left_join(tf_motif) %>%
arrange(pvalue) %>%
data.frame() %>%
left_join(Z_scoring %>% mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
filter(Type=='GFP') %>% group_by(motif_alt_id) %>% summarise(`GFP Z score` = mean(`Z score`))) %>%
left_join(Z_scoring %>% mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
filter(Type=='iPSC') %>% group_by(motif_alt_id) %>% summarise(`iPSC Z score` = mean(`Z score`))) %>%
left_join(gfp_rfp %>% mutate(TF=Gene, log2FC_GFP_RFP_RNASeq = log2FoldChange) %>% select(TF, log2FC_GFP_RFP_RNASeq))
Joining, by = "motif_alt_id"
Joining, by = "motif_alt_id"
Joining, by = "motif_alt_id"
Joining, by = "TF"
results %>% filter(`iPSC Z score` < `GFP Z score`) %>% arrange(pvalue) %>% DT::datatable()
Motifs high in iPSC
results %>% filter(`iPSC Z score` > `GFP Z score`) %>% arrange(pvalue) %>% DT::datatable()
Volcano
volcano_maker <- function(df, title){
df$Class <- 'Not significant'
df$Class[df$padj < 1e-100 & df$log2FoldChange > 1.5] <- "FDR < 1e-100 &\nlog2FC > 1.5"
df$Class[df$padj < 1e-100 & df$log2FoldChange < -1] <- "FDR < 1e-100 &\nlog2FC < -1"
df$Class <- factor(df$Class, levels=c('Not significant', "FDR < 1e-100 &\nlog2FC > 1.5", "FDR < 1e-100 &\nlog2FC < -1"))
plot <- ggplot(data=df,aes(x=log2FoldChange,y=-log10(pvalue))) +
#geom_point(aes(colour=Class, size=`Z score`), alpha=0.5) +
geom_point(aes(colour=Class), alpha = 0.5) +
scale_colour_manual(values=c("gray","darkred", "darkblue")) +
geom_text_repel(data=df %>% filter((padj < 1e-100 & log2FoldChange > 1.5) | (padj < 1e-100 & log2FoldChange < -1)),
aes(label=TF)) +
# geom_vline(aes(xintercept=-0.5),linetype="dotted") +
# geom_vline(aes(xintercept=0.5),linetype="dotted") +
scale_x_continuous(breaks=c(-2,-1.5,-1,-0.5,0,0.5,1,1.5,2)) +
ggtitle(title) + theme_minimal()
return(plot)
}
volcano_maker(results, 'GFP+ RPE vs iPSC TFBS motif counts')

Plots of the top motifs more common in GFP than iPSC
# motif ggridges plotter
# removes GFP
plotterGi <- function(motif, scale=TRUE) {
if (scale == TRUE){
sample_bootstrap_counts <- sample_bootstrap_counts %>% filter(motif_alt_id == !!motif) %>%
filter(!grepl('RFP',sample)) %>%
group_by(sample, motif_alt_id) %>%
mutate(`Z score` = scale(Count)) %>%
ungroup()
sample_bootstrap_counts %>%
filter(!grepl('RFP',sample)) %>%
#mutate(sample = factor(sample, levels=c('iPSC_IIi_9','iPSC_IIJ_10','iPSC_IIK_11','iPSC_IIL_12','RFP_IIE_1','RFP_IIF_2','RFP_IIG_3','RFP_IIH_4','GFP_IIE_5','GFP_IIF_6','GFP_IIG_7','GFP_IIH_8'))) %>%
mutate(sample = factor(sample, levels=c('iPSC_IIi_9','iPSC_IIJ_10','iPSC_IIK_11','iPSC_IIL_12','GFP_IIE_5','GFP_IIF_6','GFP_IIG_7','GFP_IIH_8'))) %>%
ggplot(aes(x=`Z score`, y=sample)) +
geom_density_ridges() +
geom_point(data = sample_bootstrap_counts %>%
filter(bootstrap == 'real', motif_alt_id == !!motif) %>%
ungroup(), aes(x=`Z score`,y=sample), colour='blue', size=2, alpha=0.5) +
scale_y_discrete(expand = c(0.01, 0)) +
theme_ridges() +
ggtitle(paste0(motif, ' (',
tf_motif %>% filter(motif_alt_id == !!motif) %>% pull(TF),
')'))
}
else {
sample_bootstrap_counts %>% filter(motif_alt_id == !!motif) %>%
filter(!grepl('RFP',sample)) %>%
#mutate(sample = factor(sample, levels=c('iPSC_IIi_9','iPSC_IIJ_10','iPSC_IIK_11','iPSC_IIL_12','RFP_IIE_1','RFP_IIF_2','RFP_IIG_3','RFP_IIH_4','GFP_IIE_5','GFP_IIF_6','GFP_IIG_7','GFP_IIH_8'))) %>%
mutate(sample = factor(sample, levels=c('iPSC_IIi_9','iPSC_IIJ_10','iPSC_IIK_11','iPSC_IIL_12','GFP_IIE_5','GFP_IIF_6','GFP_IIG_7','GFP_IIH_8'))) %>%
ggplot(aes(x=Count, y=sample)) +
geom_density_ridges() +
geom_point(data = sample_motifs %>%
filter(motif_alt_id == !!motif) %>%
group_by(sample, motif_alt_id) %>%
summarise(Count=n()) %>% ungroup(), aes(x=Count,y=sample), colour='blue', size=2, alpha=0.5) +
scale_y_discrete(expand = c(0.01, 0)) +
theme_ridges() +
ggtitle(paste0(motif, ' (',
tf_motif %>% filter(motif_alt_id == !!motif) %>% pull(TF),
')'))
}
}
plots <- list()
for (i in results %>% filter((`GFP Z score`) > 5) %>% arrange(pvalue) %>% head(n=12) %>% pull(motif_alt_id)){
plots[[i]] <- plotterGi(i, scale=T)
}
plot_grid(plotlist = plots, ncol=3)
Picking joint bandwidth of 0.176
Picking joint bandwidth of 0.168
Picking joint bandwidth of 0.166
Picking joint bandwidth of 0.159
Picking joint bandwidth of 0.159
Picking joint bandwidth of 0.168
Picking joint bandwidth of 0.162
Picking joint bandwidth of 0.167
Picking joint bandwidth of 0.163
Picking joint bandwidth of 0.164
Picking joint bandwidth of 0.166
Picking joint bandwidth of 0.166

Plots of the top motifs more common in iPSC than GFP
plots <- list()
for (i in results %>% filter((`iPSC Z score`) > 5) %>% arrange(pvalue) %>% head(n=6) %>% pull(motif_alt_id)){
plots[[i]] <- plotterGi(i, scale=T)
}
plot_grid(plotlist = plots, ncol=3)
Picking joint bandwidth of 0.217
Picking joint bandwidth of 0.223
Picking joint bandwidth of 0.22
Picking joint bandwidth of 0.241
Picking joint bandwidth of 0.241
Picking joint bandwidth of 0.243

Closest TSS for OTX2 (M5700_1.02)
Two scoring systems:
- Mean Count: number of motifs linked to gene (closest two genes within 500kb)
- Sum -log10(p value): sum of the -log10(p value) of the specificity of the motif matching. This score weighs better matching motifs more than counts.
Filtering on log2(baseMean) > 5 (~ top2/3 of genes)
plotter('M0952_1.02')

Closest TSS for JUND (M4464_1.02)
sample_closestTSS %>%
#filter(fimo_pvalue < 1e-5) %>%
filter(motif %in% c('M4464_1.02')) %>%
mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
group_by(Gene, sample, Type, motif) %>%
summarise(Count=n(), motifs = list(motif_loc), scaleP = sum(-log10(fimo_pvalue))) %>%
group_by(Gene, Type, motif) %>%
summarise(`Mean Count` = mean(Count), `Sum -log10(p value)` = sum(scaleP), motifs = list(unique(motifs))) %>%
arrange(-`Mean Count`) %>%
left_join(., gfp_rfp %>% mutate(log2FC_GFP_RFP_RNASeq = log2FoldChange, baseMean_GFP_RFP = baseMean) %>% select(Gene, log2FC_GFP_RFP_RNASeq, baseMean_GFP_RFP)) %>%
left_join(., rpe_ipsc %>% mutate(log2FC_RPE_iPSC_RNASeq = log2FoldChange, baseMean_GFP_iPSC = baseMean) %>% select(Gene, log2FC_RPE_iPSC_RNASeq, baseMean_GFP_iPSC)) %>%
filter(log2(baseMean_GFP_iPSC) > 5) %>%
select(Gene, Type, motif, `Mean Count`, `Sum -log10(p value)`, log2FC_GFP_RFP_RNASeq, log2FC_RPE_iPSC_RNASeq, motifs) %>%
head(2000) %>%
DT::datatable()
Joining, by = "Gene"
Joining, by = "Gene"
It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.htmlIt seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.html
Closest TSS for SNAI1 (M6468_1.02)
sample_closestTSS %>%
#filter(fimo_pvalue < 1e-5) %>%
filter(motif %in% c('M6468_1.02')) %>%
mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
group_by(Gene, sample, Type, motif) %>%
summarise(Count=n(), motifs = list(motif_loc), scaleP = sum(-log10(fimo_pvalue))) %>%
group_by(Gene, Type, motif) %>%
summarise(`Mean Count` = mean(Count), `Sum -log10(p value)` = sum(scaleP), motifs = list(unique(motifs))) %>%
arrange(-`Mean Count`) %>%
left_join(., gfp_rfp %>% mutate(log2FC_GFP_RFP_RNASeq = log2FoldChange, baseMean_GFP_RFP = baseMean) %>% select(Gene, log2FC_GFP_RFP_RNASeq, baseMean_GFP_RFP)) %>%
left_join(., rpe_ipsc %>% mutate(log2FC_RPE_iPSC_RNASeq = log2FoldChange, baseMean_GFP_iPSC = baseMean) %>% select(Gene, log2FC_RPE_iPSC_RNASeq, baseMean_GFP_iPSC)) %>%
filter(log2(baseMean_GFP_iPSC) > 5) %>%
select(Gene, Type, motif, `Mean Count`, `Sum -log10(p value)`, log2FC_GFP_RFP_RNASeq, log2FC_RPE_iPSC_RNASeq, motifs) %>%
head(2000) %>%
DT::datatable()
Joining, by = "Gene"
Joining, by = "Gene"
It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.htmlIt seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.html
Gene-centered approach
What motifs are associated with a particular gene?
ABCA4 for this example
sample_closestTSS %>%
mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
filter(Gene == 'ABCA4') %>%
group_by(Gene, sample, Type, motif) %>%
summarise(Count=n(), motifs = list(motif_loc), scaleP = sum(-log10(fimo_pvalue))) %>%
group_by(Gene, Type, motif) %>%
summarise(`Mean Count` = mean(Count), `Sum -log10(p value)` = sum(scaleP), motifs = list(unique(motifs))) %>%
arrange(-`Mean Count`) %>%
left_join(gfp_rfp %>% mutate(log2FC_GFP_RFP_RNASeq = log2FoldChange) %>% select(Gene, log2FC_GFP_RFP_RNASeq)) %>%
left_join(rpe_ipsc %>% mutate(log2FC_RPE_iPSC_RNASeq = log2FoldChange) %>% select(Gene, log2FC_RPE_iPSC_RNASeq)) %>%
left_join(., tf_motif %>% mutate(motif = motif_alt_id)) %>%
select(Gene, Type, motif, TF, `Mean Count`, `Sum -log10(p value)`, log2FC_GFP_RFP_RNASeq, log2FC_RPE_iPSC_RNASeq, motifs) %>%
DT::datatable()
Joining, by = "Gene"
Joining, by = "Gene"
Joining, by = "motif"
# library(tidygraph)
# library(ggraph)
#
# node_data <- sample_closestTSS %>%
# # filter(fimo_pvalue < 1e-6) %>%
# #filter(motif == 'M5700_1.02') %>%
# mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
# grepl('RFP', sample) ~ 'RFP',
# TRUE ~ 'iPSC')) %>%
# group_by(Gene, sample, Type) %>%
# summarise(Count=n(), motifs = list(motif_loc)) %>%
# group_by(Gene, Type) %>%
# summarise(`Mean Count` = mean(Count)) %>%
# arrange(-`Mean Count`) %>%
# filter(`Mean Count` > 3.9) %>%
# rowid_to_column("id")
#
#
# edge_data <- node_data %>% mutate(to = id, from = 33, weight= `Mean Count`) %>% ungroup() %>%
# select(from, to, weight, Type)
#
# node_data <- bind_rows(node_data, tibble(id = 33, Gene = 'OTX2', Type = 'TF', `Mean Count` = 0))
#
#
# routes_tidy <- tbl_graph(nodes = node_data %>% mutate(node = as.character(id)), edges = edge_data %>% mutate(from=as.character(from), to=as.character (to)), directed = TRUE)
#
# routes_igraph <- graph_from_data_frame(d = edge_data, vertices = node_data, directed = TRUE)
#
# ggraph(routes_tidy) +
# geom_edge_link(aes(width = weights, color=as.factor(Type))) +
# scale_edge_width(range = c(0.2, 2)) + geom_node_point() + theme_graph() + geom_node_text(aes(label = Gene), repel = TRUE)
# What genes have more PAX6 'associated' motifs compared from GFP to RFP
# enriched_genes <- both %>%
# # only keep one gene per motif
# group_by(motif_loc, sample, Gene) %>%
# top_n(1, distance) %>%
# ungroup() %>%
# # keep up to two genes per motif
# group_by(motif_loc, sample) %>%
# top_n(2, distance) %>%
# ungroup() %>%
# # arrange by genes most linked to motif
# group_by(Gene, sample) %>%
# summarise(Count=n(), paste(motif_loc, collapse=', ')) %>%
# ungroup() %>%
# # collapse to GFP/RFP/iPSC
# mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
# grepl('RFP', sample) ~ 'RFP',
# TRUE ~ 'iPSC')) %>%
# group_by(Gene, Type) %>%
# summarise(Total=sum(Count)) %>%
# arrange(-Total) %>%
# spread(Gene, Total) %>% t()
#
# colnames(enriched_genes) <- enriched_genes[1,]
# enriched_genes <- enriched_genes[-1,] %>% data.frame() %>% rownames_to_column('Gene') %>% mutate(GFP = as.numeric(GFP), iPSC = as.numeric(iPSC), RFP = as.numeric((RFP)))
#
# enriched_genes[is.na(enriched_genes)] <- 0
#
# enriched_genes %>% mutate(`deltaGFP <-> RFP` = GFP - RFP) %>% arrange(-`deltaGFP <-> RFP`, GFP) %>% head(1000) %>% DT::datatable(rownames = F)
## does PAX6 regulate PAX6?
#Yes, yes it does.
#GFP specific!
# both %>%
# # only keep one gene per motif
# group_by(motif_loc, sample, Gene) %>%
# top_n(1, distance) %>%
# ungroup() %>%
# # keep up to two genes per motif
# group_by(motif_loc, sample) %>%
# top_n(2, distance) %>%
# ungroup() %>%
# # arrange by genes most linked to motif
# group_by(Gene, sample) %>%
# summarise(Count=n(), `Motif Locations` = paste(motif_loc, collapse=', ')) %>%
# arrange(-Count) %>%
# filter(Gene=='PAX6')
LS0tCnRpdGxlOiBNb3RpZiAvIFRGQlMgQW5hbHlzaXMKYXV0aG9yOiBEYXZpZCBNY0dhdWdoZXkKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVktJW0tJWQiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKIyBXb3JrZmxvdyB0byBJRCBtb3RpZnMgYW5kIG1hdGNoIHRvIGdlbmVzCgooRnVsbCBpbXBsZW1lbnRhdGlvbiBpbiBTbmFrZWZpbGUpCgoxLiBEb3dubG9hZCBURiBtb3RpZnMgZnJvbSBjaXNicAoyLiBPbmx5IGtlZXAgVEYgd2hpY2ggYXJlIGFicyhsb2dGYykgPiAxIGJldHdlZW4gR0ZQL1JQRSBhbmQgaVBTQyAofjEyMDApCjMuIENoZWNrIGZvciBtb3RpZnMgaW4gbmFycm93IEFUQUMtc2VxIHBlYWtzIHdpdGggZmltbwo0LiBJZGVudGlmeSBjbG9zZXN0IDIgZ2VuZXMgKHVuZGVyIDUwMGsgYnApIHRvIGVhY2ggbW90aWYKNS4gQm9vdHN0cmFwIHN0ZXBzIDIgYW5kIDMgMjUwIHRpbWVzIGVhY2ggdG8gZ2V0IGJhY2tncm91bmQgcmF0ZQoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCByZXN1bHRzPSdoaWRlJ30KIyBMb2FkIExpYnJhcmllcyB3aXRob3V0IHByaW50aW5nIGFueSB3YXJuaW5ncyBvciBtZXNzYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3JlcGVsKQoKbG9hZCgnL1ZvbHVtZXMvZGF0YS9wcm9qZWN0cy9uZWkvaHVmbmFnZWwvaVBTQ19SUEVfQVRBQ19TZXEvUmRhdGEvZGRzLlJkYXRhJykKbG9hZCgnL1ZvbHVtZXMvZGF0YS9wcm9qZWN0cy9uZWkvaHVmbmFnZWwvaVBTQ19SUEVfQVRBQ19TZXEvUmRhdGEvc2FtcGxlX2Jvb3RzdHJhcF9jb3VudHMuUmRhdGEnKQpsb2FkKCcvVm9sdW1lcy9kYXRhL3Byb2plY3RzL25laS9odWZuYWdlbC9pUFNDX1JQRV9BVEFDX1NlcS9SZGF0YS9zYW1wbGVfY2xvc2VzdFRTUy5SZGF0YScpCmxvYWQoJ34vZ2l0L2lwc2NfcnBlX2F0YWMvZGF0YS90Zl9tb3RpZi5SZGF0YScpCgpgYGAKCiMgREVTZXEyLWJhc2VkIGFuYWx5c2lzCkkgcmVhbGl6ZWQgdGhhdCBtb3RpZnMgYnkgc2FtcGxlcyBpcyBzaW1pbGFyIHRvIGdlbmVzIGJ5IHNhbXBsZXMKCgpHRlAgdnMgaVBTQwpgYGB7cn0KCmRkcyRUeXBlIDwtIHJlbGV2ZWwoZGRzJFR5cGUsIHJlZj0nUkZQJykKcmVzX0dGUF9pUFNDIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJUeXBlIiwiR0ZQIiwiaVBTQyIpKSAKcmVzX0dGUF9SRlAgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0PWMoIlR5cGUiLCJHRlAiLCJSRlAiKSkgCgojcGxvdE1BKHJlc19HRlBfaVBTQykKI3Bsb3QoaGlzdChyZXNfR0ZQX2lQU0MkcHZhbHVlLCBicmVha3M9MTAwMCkpCgpgYGAKCmBgYHtyfQojIExvYWQgaW4gUk5BLXNlcSBkYXRhCmdmcF9yZnAgPC0gcmVhZF9jc3YoJ34vZ2l0L2lwc2NfcnBlX1JOQS1zZXEvZGF0YS9HRlBfdnNfUkZQLnJlc3VsdHMuY3N2JykKcnBlX2lwc2MgPC0gcmVhZF9jc3YoJ34vZ2l0L2lwc2NfcnBlX1JOQS1zZXEvZGF0YS9SUEVfdnNfaVBTQy5yZXN1bHRzLmNzdicpCiNnZnBfcmZwICU+JSBoZWFkKCkKYGBgCgojIyBUYWJsZSBvZiByZXN1bHRzCgojIyMgTW90aWZzIGhpZ2ggaW4gR0ZQCmBgYHtyfQojIGNhbGN1bGF0ZSB6IHNjb3JlcyBmb3Igc2FtcGxlX2Jvb3RzdHJhcF9jb3VudHMKWl9zY29yaW5nIDwtIHNhbXBsZV9ib290c3RyYXBfY291bnRzICU+JSAKICAjIGNvbGxhcHNlIGJ5IFR5cGUsIG1vdGlmLCBhbmQgd2hldGhlciBib290c3RyYXAgb3IgcmVhbAogIGdyb3VwX2J5KHNhbXBsZSwgbW90aWZfYWx0X2lkKSAlPiUgCiAgbXV0YXRlKGBaIHNjb3JlYCA9IHNjYWxlKENvdW50KSkgJT4lIAogIGZpbHRlcihib290c3RyYXAgPT0gJ3JlYWwnKSAlPiUgCiAgc2VsZWN0KC1ib290c3RyYXAsIC1URikgJT4lIAogIGxlZnRfam9pbih0Zl9tb3RpZikKCiNaX3Njb3JpbmcgJT4lIGhlYWQoKQpgYGAKClRoZSBgWiBzY29yZWAgaXMgdGhlIG51bWJlciBvZiBzdGFuZGFyZCBkZXZpYXRpb24gb2YgbW90aWZzIGZvdW5kIGluIHRoZSBzYW1wbGUgcGVha3MgYWJvdmUgdGhlIHJhbmRvbSBzZXQgb2YgcGVha3MKYGBge3J9CnJlc3VsdHMgPC0gcmVzX0dGUF9pUFNDICU+JSAKICBkYXRhLmZyYW1lKCkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbignbW90aWZfYWx0X2lkJykgJT4lIAogIGxlZnRfam9pbih0Zl9tb3RpZikgJT4lIAogIGFycmFuZ2UocHZhbHVlKSAlPiUgCiAgZGF0YS5mcmFtZSgpICU+JSAKICBsZWZ0X2pvaW4oWl9zY29yaW5nICU+JSAgbXV0YXRlKFR5cGUgPSBjYXNlX3doZW4oZ3JlcGwoJ0dGUCcsIHNhbXBsZSkgfiAnR0ZQJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCdSRlAnLCBzYW1wbGUpIH4gJ1JGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSkgJT4lIAogICAgICAgICAgICAgIGZpbHRlcihUeXBlPT0nR0ZQJykgJT4lIGdyb3VwX2J5KG1vdGlmX2FsdF9pZCkgJT4lIHN1bW1hcmlzZShgR0ZQIFogc2NvcmVgID0gbWVhbihgWiBzY29yZWApKSkgJT4lIAogIGxlZnRfam9pbihaX3Njb3JpbmcgJT4lICBtdXRhdGUoVHlwZSA9IGNhc2Vfd2hlbihncmVwbCgnR0ZQJywgc2FtcGxlKSB+ICdHRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCdSRlAnLCBzYW1wbGUpIH4gJ1JGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdpUFNDJykpICU+JSAKICAgICAgICAgICAgICBmaWx0ZXIoVHlwZT09J2lQU0MnKSAlPiUgZ3JvdXBfYnkobW90aWZfYWx0X2lkKSAlPiUgc3VtbWFyaXNlKGBpUFNDIFogc2NvcmVgID0gbWVhbihgWiBzY29yZWApKSkgJT4lIAogIGxlZnRfam9pbihnZnBfcmZwICU+JSBtdXRhdGUoVEY9R2VuZSwgbG9nMkZDX0dGUF9SRlBfUk5BU2VxID0gbG9nMkZvbGRDaGFuZ2UpICU+JSBzZWxlY3QoVEYsIGxvZzJGQ19HRlBfUkZQX1JOQVNlcSkpIApyZXN1bHRzICU+JSBmaWx0ZXIoYGlQU0MgWiBzY29yZWAgPCBgR0ZQIFogc2NvcmVgKSAlPiUgYXJyYW5nZShwdmFsdWUpICU+JSBEVDo6ZGF0YXRhYmxlKCkKYGBgCiMjIyBNb3RpZnMgaGlnaCBpbiBpUFNDCmBgYHtyfQpyZXN1bHRzICU+JSBmaWx0ZXIoYGlQU0MgWiBzY29yZWAgPiBgR0ZQIFogc2NvcmVgKSAlPiUgYXJyYW5nZShwdmFsdWUpICU+JSBEVDo6ZGF0YXRhYmxlKCkKYGBgCgojIyBWb2xjYW5vCmBgYHtyfQp2b2xjYW5vX21ha2VyIDwtIGZ1bmN0aW9uKGRmLCB0aXRsZSl7CiAgZGYkQ2xhc3MgPC0gJ05vdCBzaWduaWZpY2FudCcKICBkZiRDbGFzc1tkZiRwYWRqIDwgMWUtMTAwICYgZGYkbG9nMkZvbGRDaGFuZ2UgPiAxLjVdIDwtICJGRFIgPCAxZS0xMDAgJlxubG9nMkZDID4gMS41IgogIGRmJENsYXNzW2RmJHBhZGogPCAxZS0xMDAgJiBkZiRsb2cyRm9sZENoYW5nZSA8IC0xXSA8LSAiRkRSIDwgMWUtMTAwICZcbmxvZzJGQyA8IC0xIgogIGRmJENsYXNzIDwtIGZhY3RvcihkZiRDbGFzcywgbGV2ZWxzPWMoJ05vdCBzaWduaWZpY2FudCcsICJGRFIgPCAxZS0xMDAgJlxubG9nMkZDID4gMS41IiwgIkZEUiA8IDFlLTEwMCAmXG5sb2cyRkMgPCAtMSIpKQogIHBsb3QgPC0gZ2dwbG90KGRhdGE9ZGYsYWVzKHg9bG9nMkZvbGRDaGFuZ2UseT0tbG9nMTAocHZhbHVlKSkpICsgCiAgICAjZ2VvbV9wb2ludChhZXMoY29sb3VyPUNsYXNzLCBzaXplPWBaIHNjb3JlYCksIGFscGhhPTAuNSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3VyPUNsYXNzKSwgYWxwaGEgPSAwLjUpICsgCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJncmF5IiwiZGFya3JlZCIsICJkYXJrYmx1ZSIpKSArIAogICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9ZGYgJT4lIGZpbHRlcigocGFkaiA8IDFlLTEwMCAgJiBsb2cyRm9sZENoYW5nZSA+IDEuNSkgfCAocGFkaiA8IDFlLTEwMCAmIGxvZzJGb2xkQ2hhbmdlIDwgLTEpKSwgCiAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsPVRGKSkgKwogICAgIyBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PS0wLjUpLGxpbmV0eXBlPSJkb3R0ZWQiKSArCiAgICAjIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9MC41KSxsaW5ldHlwZT0iZG90dGVkIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKC0yLC0xLjUsLTEsLTAuNSwwLDAuNSwxLDEuNSwyKSkgKwogICAgZ2d0aXRsZSh0aXRsZSkgKyB0aGVtZV9taW5pbWFsKCkKICByZXR1cm4ocGxvdCkKfQp2b2xjYW5vX21ha2VyKHJlc3VsdHMsICdHRlArIFJQRSB2cyBpUFNDIFRGQlMgbW90aWYgY291bnRzJykKCmBgYAoKCiMjIFBsb3RzIG9mIHRoZSB0b3AgbW90aWZzIG1vcmUgY29tbW9uIGluIEdGUCB0aGFuIGlQU0MKCmBgYHtyfQojIG1vdGlmIGdncmlkZ2VzIHBsb3R0ZXIKIyByZW1vdmVzIEdGUCAKcGxvdHRlckdpIDwtIGZ1bmN0aW9uKG1vdGlmLCBzY2FsZT1UUlVFKSB7CiAgaWYgKHNjYWxlID09IFRSVUUpewogICAgc2FtcGxlX2Jvb3RzdHJhcF9jb3VudHMgPC0gc2FtcGxlX2Jvb3RzdHJhcF9jb3VudHMgJT4lIGZpbHRlcihtb3RpZl9hbHRfaWQgPT0gISFtb3RpZikgJT4lIAogICAgICBmaWx0ZXIoIWdyZXBsKCdSRlAnLHNhbXBsZSkpICU+JSAKICAgICAgZ3JvdXBfYnkoc2FtcGxlLCBtb3RpZl9hbHRfaWQpICU+JSAKICAgICAgbXV0YXRlKGBaIHNjb3JlYCA9IHNjYWxlKENvdW50KSkgJT4lIAogICAgICB1bmdyb3VwKCkgCiAgICBzYW1wbGVfYm9vdHN0cmFwX2NvdW50cyAlPiUgCiAgICAgIGZpbHRlcighZ3JlcGwoJ1JGUCcsc2FtcGxlKSkgJT4lIAogICAgICAjbXV0YXRlKHNhbXBsZSA9IGZhY3RvcihzYW1wbGUsIGxldmVscz1jKCdpUFNDX0lJaV85JywnaVBTQ19JSUpfMTAnLCdpUFNDX0lJS18xMScsJ2lQU0NfSUlMXzEyJywnUkZQX0lJRV8xJywnUkZQX0lJRl8yJywnUkZQX0lJR18zJywnUkZQX0lJSF80JywnR0ZQX0lJRV81JywnR0ZQX0lJRl82JywnR0ZQX0lJR183JywnR0ZQX0lJSF84JykpKSAlPiUgCiAgICAgIG11dGF0ZShzYW1wbGUgPSBmYWN0b3Ioc2FtcGxlLCBsZXZlbHM9YygnaVBTQ19JSWlfOScsJ2lQU0NfSUlKXzEwJywnaVBTQ19JSUtfMTEnLCdpUFNDX0lJTF8xMicsJ0dGUF9JSUVfNScsJ0dGUF9JSUZfNicsJ0dGUF9JSUdfNycsJ0dGUF9JSUhfOCcpKSkgJT4lIAogICAgICBnZ3Bsb3QoYWVzKHg9YFogc2NvcmVgLCB5PXNhbXBsZSkpICsgCiAgICAgIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHNhbXBsZV9ib290c3RyYXBfY291bnRzICU+JSAKICAgICAgICAgICAgICAgICAgIGZpbHRlcihib290c3RyYXAgPT0gJ3JlYWwnLCBtb3RpZl9hbHRfaWQgPT0gISFtb3RpZikgJT4lIAogICAgICAgICAgICAgICAgICAgdW5ncm91cCgpLCBhZXMoeD1gWiBzY29yZWAseT1zYW1wbGUpLCBjb2xvdXI9J2JsdWUnLCBzaXplPTIsIGFscGhhPTAuNSkgKwogICAgICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsKICAgICAgdGhlbWVfcmlkZ2VzKCkgKyAKICAgICAgZ2d0aXRsZShwYXN0ZTAobW90aWYsICcgKCcsIAogICAgICAgICAgICAgICAgICAgICB0Zl9tb3RpZiAlPiUgZmlsdGVyKG1vdGlmX2FsdF9pZCA9PSAhIW1vdGlmKSAlPiUgcHVsbChURiksCiAgICAgICAgICAgICAgICAgICAgICcpJykpCiAgfQogIGVsc2UgewogICAgc2FtcGxlX2Jvb3RzdHJhcF9jb3VudHMgJT4lIGZpbHRlcihtb3RpZl9hbHRfaWQgPT0gISFtb3RpZikgJT4lIAogICAgICBmaWx0ZXIoIWdyZXBsKCdSRlAnLHNhbXBsZSkpICU+JSAKICAgICAgI211dGF0ZShzYW1wbGUgPSBmYWN0b3Ioc2FtcGxlLCBsZXZlbHM9YygnaVBTQ19JSWlfOScsJ2lQU0NfSUlKXzEwJywnaVBTQ19JSUtfMTEnLCdpUFNDX0lJTF8xMicsJ1JGUF9JSUVfMScsJ1JGUF9JSUZfMicsJ1JGUF9JSUdfMycsJ1JGUF9JSUhfNCcsJ0dGUF9JSUVfNScsJ0dGUF9JSUZfNicsJ0dGUF9JSUdfNycsJ0dGUF9JSUhfOCcpKSkgJT4lIAogICAgICBtdXRhdGUoc2FtcGxlID0gZmFjdG9yKHNhbXBsZSwgbGV2ZWxzPWMoJ2lQU0NfSUlpXzknLCdpUFNDX0lJSl8xMCcsJ2lQU0NfSUlLXzExJywnaVBTQ19JSUxfMTInLCdHRlBfSUlFXzUnLCdHRlBfSUlGXzYnLCdHRlBfSUlHXzcnLCdHRlBfSUlIXzgnKSkpICU+JSAKICAgICAgZ2dwbG90KGFlcyh4PUNvdW50LCB5PXNhbXBsZSkpICsgCiAgICAgIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHNhbXBsZV9tb3RpZnMgJT4lIAogICAgICAgICAgICAgICAgICAgZmlsdGVyKG1vdGlmX2FsdF9pZCA9PSAhIW1vdGlmKSAlPiUgCiAgICAgICAgICAgICAgICAgICBncm91cF9ieShzYW1wbGUsIG1vdGlmX2FsdF9pZCkgJT4lCiAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUgdW5ncm91cCgpLCBhZXMoeD1Db3VudCx5PXNhbXBsZSksIGNvbG91cj0nYmx1ZScsIHNpemU9MiwgYWxwaGE9MC41KSArCiAgICAgIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogICAgICB0aGVtZV9yaWRnZXMoKSArIAogICAgICBnZ3RpdGxlKHBhc3RlMChtb3RpZiwgJyAoJywgCiAgICAgICAgICAgICAgICAgICAgIHRmX21vdGlmICU+JSBmaWx0ZXIobW90aWZfYWx0X2lkID09ICEhbW90aWYpICU+JSBwdWxsKFRGKSwKICAgICAgICAgICAgICAgICAgICAgJyknKSkKICB9Cn0KYGBgCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0KcGxvdHMgPC0gbGlzdCgpCmZvciAoaSBpbiByZXN1bHRzICU+JSBmaWx0ZXIoKGBHRlAgWiBzY29yZWApID4gNSkgJT4lIGFycmFuZ2UocHZhbHVlKSAlPiUgaGVhZChuPTEyKSAlPiUgcHVsbChtb3RpZl9hbHRfaWQpKXsKICBwbG90c1tbaV1dIDwtIHBsb3R0ZXJHaShpLCBzY2FsZT1UKQp9CgoKcGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdHMsIG5jb2w9MykKYGBgCgojIyBQbG90cyBvZiB0aGUgdG9wIG1vdGlmcyBtb3JlIGNvbW1vbiBpbiBpUFNDIHRoYW4gR0ZQCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0KcGxvdHMgPC0gbGlzdCgpCmZvciAoaSBpbiByZXN1bHRzICU+JSBmaWx0ZXIoKGBpUFNDIFogc2NvcmVgKSA+IDUpICU+JSBhcnJhbmdlKHB2YWx1ZSkgJT4lIGhlYWQobj02KSAlPiUgcHVsbChtb3RpZl9hbHRfaWQpKXsKICBwbG90c1tbaV1dIDwtIHBsb3R0ZXJHaShpLCBzY2FsZT1UKQp9CgoKcGxvdF9ncmlkKHBsb3RsaXN0ID0gcGxvdHMsIG5jb2w9MykKYGBgCgoKCiMjIENsb3Nlc3QgVFNTIGZvciBPVFgyIChNNTcwMF8xLjAyKQpUd28gc2NvcmluZyBzeXN0ZW1zOgoKMS4gTWVhbiBDb3VudDogbnVtYmVyIG9mIG1vdGlmcyBsaW5rZWQgdG8gZ2VuZSAoY2xvc2VzdCB0d28gZ2VuZXMgd2l0aGluIDUwMGtiKQoyLiBTdW0gLWxvZzEwKHAgdmFsdWUpOiBzdW0gb2YgdGhlIC1sb2cxMChwIHZhbHVlKSBvZiB0aGUgc3BlY2lmaWNpdHkgb2YgdGhlIG1vdGlmIG1hdGNoaW5nLiBUaGlzIHNjb3JlIHdlaWdocyBiZXR0ZXIgbWF0Y2hpbmcgbW90aWZzIG1vcmUgdGhhbiBjb3VudHMuIAoKRmlsdGVyaW5nIG9uIGxvZzIoYmFzZU1lYW4pID4gNSAofiB0b3AyLzMgb2YgZ2VuZXMpCgpgYGB7cn0Kc2FtcGxlX2Nsb3Nlc3RUU1MgJT4lIAogIyBmaWx0ZXIoZmltb19wdmFsdWUgPCAxZS02KSAlPiUgCiAgZmlsdGVyKG1vdGlmICVpbiUgYygnTTU3MDBfMS4wMicpKSAlPiUgCiAgbXV0YXRlKFR5cGUgPSBjYXNlX3doZW4oZ3JlcGwoJ0dGUCcsIHNhbXBsZSkgfiAnR0ZQJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoJ1JGUCcsIHNhbXBsZSkgfiAnUkZQJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdpUFNDJykpICU+JSAKICBncm91cF9ieShHZW5lLCBzYW1wbGUsIFR5cGUsIG1vdGlmKSAlPiUKICBzdW1tYXJpc2UoQ291bnQ9bigpLCBtb3RpZnMgPSBsaXN0KG1vdGlmX2xvYyksIHNjYWxlUCA9IHN1bSgtbG9nMTAoZmltb19wdmFsdWUpKSkgJT4lCiAgZ3JvdXBfYnkoR2VuZSwgVHlwZSwgbW90aWYpICU+JSAKICBzdW1tYXJpc2UoYE1lYW4gQ291bnRgID0gbWVhbihDb3VudCksIGBTdW0gLWxvZzEwKHAgdmFsdWUpYCA9IHN1bShzY2FsZVApLCBtb3RpZnMgPSBsaXN0KHVuaXF1ZShtb3RpZnMpKSkgJT4lIAogIGFycmFuZ2UoLWBNZWFuIENvdW50YCkgJT4lIAogIGxlZnRfam9pbiguLCBnZnBfcmZwICU+JSBtdXRhdGUobG9nMkZDX0dGUF9SRlBfUk5BU2VxID0gbG9nMkZvbGRDaGFuZ2UsIGJhc2VNZWFuX0dGUF9SRlAgPSBiYXNlTWVhbikgJT4lIHNlbGVjdChHZW5lLCBsb2cyRkNfR0ZQX1JGUF9STkFTZXEsIGJhc2VNZWFuX0dGUF9SRlApKSAlPiUgCiAgbGVmdF9qb2luKC4sIHJwZV9pcHNjICU+JSBtdXRhdGUobG9nMkZDX1JQRV9pUFNDX1JOQVNlcSA9IGxvZzJGb2xkQ2hhbmdlLCBiYXNlTWVhbl9HRlBfaVBTQyA9IGJhc2VNZWFuKSAlPiUgc2VsZWN0KEdlbmUsIGxvZzJGQ19SUEVfaVBTQ19STkFTZXEsIGJhc2VNZWFuX0dGUF9pUFNDKSkgJT4lIAogIGZpbHRlcihsb2cyKGJhc2VNZWFuX0dGUF9pUFNDKSA+IDUpICU+JSAKICBzZWxlY3QoR2VuZSwgVHlwZSwgbW90aWYsIGBNZWFuIENvdW50YCwgYFN1bSAtbG9nMTAocCB2YWx1ZSlgLCBsb2cyRkNfR0ZQX1JGUF9STkFTZXEsIGxvZzJGQ19SUEVfaVBTQ19STkFTZXEsIG1vdGlmcykgJT4lIAogIGhlYWQoMjAwMCkgJT4lIAogIERUOjpkYXRhdGFibGUoKQpgYGAKCiMjIENsb3Nlc3QgVFNTIGZvciBKVU5EIChNNDQ2NF8xLjAyKQpgYGB7cn0Kc2FtcGxlX2Nsb3Nlc3RUU1MgJT4lIAogICNmaWx0ZXIoZmltb19wdmFsdWUgPCAxZS01KSAlPiUgCiAgZmlsdGVyKG1vdGlmICVpbiUgYygnTTQ0NjRfMS4wMicpKSAlPiUgCiBtdXRhdGUoVHlwZSA9IGNhc2Vfd2hlbihncmVwbCgnR0ZQJywgc2FtcGxlKSB+ICdHRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgnUkZQJywgc2FtcGxlKSB+ICdSRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSkgJT4lIAogIGdyb3VwX2J5KEdlbmUsIHNhbXBsZSwgVHlwZSwgbW90aWYpICU+JQogIHN1bW1hcmlzZShDb3VudD1uKCksIG1vdGlmcyA9IGxpc3QobW90aWZfbG9jKSwgc2NhbGVQID0gc3VtKC1sb2cxMChmaW1vX3B2YWx1ZSkpKSAlPiUKICBncm91cF9ieShHZW5lLCBUeXBlLCBtb3RpZikgJT4lIAogIHN1bW1hcmlzZShgTWVhbiBDb3VudGAgPSBtZWFuKENvdW50KSwgYFN1bSAtbG9nMTAocCB2YWx1ZSlgID0gc3VtKHNjYWxlUCksIG1vdGlmcyA9IGxpc3QodW5pcXVlKG1vdGlmcykpKSAlPiUgCiAgYXJyYW5nZSgtYE1lYW4gQ291bnRgKSAlPiUgCiAgbGVmdF9qb2luKC4sIGdmcF9yZnAgJT4lIG11dGF0ZShsb2cyRkNfR0ZQX1JGUF9STkFTZXEgPSBsb2cyRm9sZENoYW5nZSwgYmFzZU1lYW5fR0ZQX1JGUCA9IGJhc2VNZWFuKSAlPiUgc2VsZWN0KEdlbmUsIGxvZzJGQ19HRlBfUkZQX1JOQVNlcSwgYmFzZU1lYW5fR0ZQX1JGUCkpICU+JSAKICBsZWZ0X2pvaW4oLiwgcnBlX2lwc2MgJT4lIG11dGF0ZShsb2cyRkNfUlBFX2lQU0NfUk5BU2VxID0gbG9nMkZvbGRDaGFuZ2UsIGJhc2VNZWFuX0dGUF9pUFNDID0gYmFzZU1lYW4pICU+JSBzZWxlY3QoR2VuZSwgbG9nMkZDX1JQRV9pUFNDX1JOQVNlcSwgYmFzZU1lYW5fR0ZQX2lQU0MpKSAlPiUgCiAgZmlsdGVyKGxvZzIoYmFzZU1lYW5fR0ZQX2lQU0MpID4gNSkgJT4lIAogIHNlbGVjdChHZW5lLCBUeXBlLCBtb3RpZiwgYE1lYW4gQ291bnRgLCBgU3VtIC1sb2cxMChwIHZhbHVlKWAsIGxvZzJGQ19HRlBfUkZQX1JOQVNlcSwgbG9nMkZDX1JQRV9pUFNDX1JOQVNlcSwgbW90aWZzKSAlPiUgCiAgaGVhZCgyMDAwKSAlPiUgCiAgRFQ6OmRhdGF0YWJsZSgpCmBgYAoKIyMgQ2xvc2VzdCBUU1MgZm9yIFNOQUkxIChNNjQ2OF8xLjAyKQpgYGB7cn0Kc2FtcGxlX2Nsb3Nlc3RUU1MgJT4lIAogICNmaWx0ZXIoZmltb19wdmFsdWUgPCAxZS01KSAlPiUgCiAgZmlsdGVyKG1vdGlmICVpbiUgYygnTTY0NjhfMS4wMicpKSAlPiUgCiBtdXRhdGUoVHlwZSA9IGNhc2Vfd2hlbihncmVwbCgnR0ZQJywgc2FtcGxlKSB+ICdHRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgnUkZQJywgc2FtcGxlKSB+ICdSRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSkgJT4lIAogIGdyb3VwX2J5KEdlbmUsIHNhbXBsZSwgVHlwZSwgbW90aWYpICU+JQogIHN1bW1hcmlzZShDb3VudD1uKCksIG1vdGlmcyA9IGxpc3QobW90aWZfbG9jKSwgc2NhbGVQID0gc3VtKC1sb2cxMChmaW1vX3B2YWx1ZSkpKSAlPiUKICBncm91cF9ieShHZW5lLCBUeXBlLCBtb3RpZikgJT4lIAogIHN1bW1hcmlzZShgTWVhbiBDb3VudGAgPSBtZWFuKENvdW50KSwgYFN1bSAtbG9nMTAocCB2YWx1ZSlgID0gc3VtKHNjYWxlUCksIG1vdGlmcyA9IGxpc3QodW5pcXVlKG1vdGlmcykpKSAlPiUgCiAgYXJyYW5nZSgtYE1lYW4gQ291bnRgKSAlPiUgCiAgbGVmdF9qb2luKC4sIGdmcF9yZnAgJT4lIG11dGF0ZShsb2cyRkNfR0ZQX1JGUF9STkFTZXEgPSBsb2cyRm9sZENoYW5nZSwgYmFzZU1lYW5fR0ZQX1JGUCA9IGJhc2VNZWFuKSAlPiUgc2VsZWN0KEdlbmUsIGxvZzJGQ19HRlBfUkZQX1JOQVNlcSwgYmFzZU1lYW5fR0ZQX1JGUCkpICU+JSAKICBsZWZ0X2pvaW4oLiwgcnBlX2lwc2MgJT4lIG11dGF0ZShsb2cyRkNfUlBFX2lQU0NfUk5BU2VxID0gbG9nMkZvbGRDaGFuZ2UsIGJhc2VNZWFuX0dGUF9pUFNDID0gYmFzZU1lYW4pICU+JSBzZWxlY3QoR2VuZSwgbG9nMkZDX1JQRV9pUFNDX1JOQVNlcSwgYmFzZU1lYW5fR0ZQX2lQU0MpKSAlPiUgCiAgZmlsdGVyKGxvZzIoYmFzZU1lYW5fR0ZQX2lQU0MpID4gNSkgJT4lIAogIHNlbGVjdChHZW5lLCBUeXBlLCBtb3RpZiwgYE1lYW4gQ291bnRgLCBgU3VtIC1sb2cxMChwIHZhbHVlKWAsIGxvZzJGQ19HRlBfUkZQX1JOQVNlcSwgbG9nMkZDX1JQRV9pUFNDX1JOQVNlcSwgbW90aWZzKSAlPiUgCiAgaGVhZCgyMDAwKSAlPiUgCiAgRFQ6OmRhdGF0YWJsZSgpCmBgYAoKCiMjIEdlbmUtY2VudGVyZWQgYXBwcm9hY2gKV2hhdCBtb3RpZnMgYXJlIGFzc29jaWF0ZWQgd2l0aCBhIHBhcnRpY3VsYXIgZ2VuZT8KCkFCQ0E0IGZvciB0aGlzIGV4YW1wbGUKYGBge3J9CnNhbXBsZV9jbG9zZXN0VFNTICU+JQogIG11dGF0ZShUeXBlID0gY2FzZV93aGVuKGdyZXBsKCdHRlAnLCBzYW1wbGUpIH4gJ0dGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoJ1JGUCcsIHNhbXBsZSkgfiAnUkZQJywKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSkgJT4lIAogIGZpbHRlcihHZW5lID09ICdBQkNBNCcpICU+JSAKICBncm91cF9ieShHZW5lLCBzYW1wbGUsIFR5cGUsIG1vdGlmKSAlPiUKICBzdW1tYXJpc2UoQ291bnQ9bigpLCBtb3RpZnMgPSBsaXN0KG1vdGlmX2xvYyksIHNjYWxlUCA9IHN1bSgtbG9nMTAoZmltb19wdmFsdWUpKSkgJT4lIAogIGdyb3VwX2J5KEdlbmUsIFR5cGUsIG1vdGlmKSAlPiUgCiAgc3VtbWFyaXNlKGBNZWFuIENvdW50YCA9IG1lYW4oQ291bnQpLCBgU3VtIC1sb2cxMChwIHZhbHVlKWAgPSBzdW0oc2NhbGVQKSwgbW90aWZzID0gbGlzdCh1bmlxdWUobW90aWZzKSkpICU+JSAKICBhcnJhbmdlKC1gTWVhbiBDb3VudGApICU+JSAKICBsZWZ0X2pvaW4oZ2ZwX3JmcCAlPiUgbXV0YXRlKGxvZzJGQ19HRlBfUkZQX1JOQVNlcSA9IGxvZzJGb2xkQ2hhbmdlKSAlPiUgc2VsZWN0KEdlbmUsIGxvZzJGQ19HRlBfUkZQX1JOQVNlcSkpICU+JSAKICBsZWZ0X2pvaW4ocnBlX2lwc2MgJT4lIG11dGF0ZShsb2cyRkNfUlBFX2lQU0NfUk5BU2VxID0gbG9nMkZvbGRDaGFuZ2UpICU+JSBzZWxlY3QoR2VuZSwgbG9nMkZDX1JQRV9pUFNDX1JOQVNlcSkpICU+JSAKICBsZWZ0X2pvaW4oLiwgdGZfbW90aWYgJT4lIG11dGF0ZShtb3RpZiA9IG1vdGlmX2FsdF9pZCkpICU+JSAKICBzZWxlY3QoR2VuZSwgVHlwZSwgbW90aWYsIFRGLCBgTWVhbiBDb3VudGAsIGBTdW0gLWxvZzEwKHAgdmFsdWUpYCwgbG9nMkZDX0dGUF9SRlBfUk5BU2VxLCBsb2cyRkNfUlBFX2lQU0NfUk5BU2VxLCBtb3RpZnMpICU+JSAgIAogIERUOjpkYXRhdGFibGUoKQoKYGBgCgoKYGBge3J9CiMgbGlicmFyeSh0aWR5Z3JhcGgpCiMgbGlicmFyeShnZ3JhcGgpCiMgCiMgbm9kZV9kYXRhIDwtIHNhbXBsZV9jbG9zZXN0VFNTICU+JSAKIyAgIyBmaWx0ZXIoZmltb19wdmFsdWUgPCAxZS02KSAlPiUgCiMgICAjZmlsdGVyKG1vdGlmID09ICdNNTcwMF8xLjAyJykgJT4lIAojICAgbXV0YXRlKFR5cGUgPSBjYXNlX3doZW4oZ3JlcGwoJ0dGUCcsIHNhbXBsZSkgfiAnR0ZQJywKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgnUkZQJywgc2FtcGxlKSB+ICdSRlAnLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAnaVBTQycpKSAlPiUgCiMgICBncm91cF9ieShHZW5lLCBzYW1wbGUsIFR5cGUpICU+JQojICAgc3VtbWFyaXNlKENvdW50PW4oKSwgbW90aWZzID0gbGlzdChtb3RpZl9sb2MpKSAlPiUKIyAgIGdyb3VwX2J5KEdlbmUsIFR5cGUpICU+JSAKIyAgIHN1bW1hcmlzZShgTWVhbiBDb3VudGAgPSBtZWFuKENvdW50KSkgJT4lIAojICAgYXJyYW5nZSgtYE1lYW4gQ291bnRgKSAlPiUgCiMgICBmaWx0ZXIoYE1lYW4gQ291bnRgID4gMy45KSAlPiUgCiMgICByb3dpZF90b19jb2x1bW4oImlkIikKIyAKIyAKIyBlZGdlX2RhdGEgPC0gbm9kZV9kYXRhICU+JSBtdXRhdGUodG8gPSBpZCwgZnJvbSA9IDMzLCB3ZWlnaHQ9IGBNZWFuIENvdW50YCkgJT4lIHVuZ3JvdXAoKSAlPiUgCiMgICBzZWxlY3QoZnJvbSwgdG8sIHdlaWdodCwgVHlwZSkKIyAKIyBub2RlX2RhdGEgPC0gYmluZF9yb3dzKG5vZGVfZGF0YSwgdGliYmxlKGlkID0gMzMsIEdlbmUgPSAnT1RYMicsIFR5cGUgPSAnVEYnLCBgTWVhbiBDb3VudGAgPSAwKSkKIyAKIyAKIyByb3V0ZXNfdGlkeSA8LSB0YmxfZ3JhcGgobm9kZXMgPSBub2RlX2RhdGEgJT4lIG11dGF0ZShub2RlID0gYXMuY2hhcmFjdGVyKGlkKSksIGVkZ2VzID0gZWRnZV9kYXRhICU+JSBtdXRhdGUoZnJvbT1hcy5jaGFyYWN0ZXIoZnJvbSksIHRvPWFzLmNoYXJhY3RlciAodG8pKSwgZGlyZWN0ZWQgPSBUUlVFKQojIAojIHJvdXRlc19pZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGQgPSBlZGdlX2RhdGEsIHZlcnRpY2VzID0gbm9kZV9kYXRhLCBkaXJlY3RlZCA9IFRSVUUpCiMgCiMgZ2dyYXBoKHJvdXRlc190aWR5KSArIAojICAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gd2VpZ2h0cywgY29sb3I9YXMuZmFjdG9yKFR5cGUpKSkgKyAKIyAgIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAuMiwgMikpICsgZ2VvbV9ub2RlX3BvaW50KCkgKyB0aGVtZV9ncmFwaCgpICsgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gR2VuZSksIHJlcGVsID0gVFJVRSkgCmBgYAoKYGBge3J9CgojIFdoYXQgZ2VuZXMgaGF2ZSBtb3JlIFBBWDYgJ2Fzc29jaWF0ZWQnIG1vdGlmcyBjb21wYXJlZCBmcm9tIEdGUCB0byBSRlAKCiMgZW5yaWNoZWRfZ2VuZXMgPC0gYm90aCAlPiUgCiMgICAjIG9ubHkga2VlcCBvbmUgZ2VuZSBwZXIgbW90aWYKIyAgIGdyb3VwX2J5KG1vdGlmX2xvYywgc2FtcGxlLCBHZW5lKSAlPiUgCiMgICB0b3BfbigxLCBkaXN0YW5jZSkgJT4lIAojICAgdW5ncm91cCgpICU+JSAKIyAgICMga2VlcCB1cCB0byB0d28gZ2VuZXMgcGVyIG1vdGlmCiMgICBncm91cF9ieShtb3RpZl9sb2MsIHNhbXBsZSkgJT4lIAojICAgdG9wX24oMiwgZGlzdGFuY2UpICU+JSAKIyAgIHVuZ3JvdXAoKSAlPiUgCiMgICAjIGFycmFuZ2UgYnkgZ2VuZXMgbW9zdCBsaW5rZWQgdG8gbW90aWYgIAojICAgZ3JvdXBfYnkoR2VuZSwgc2FtcGxlKSAlPiUgCiMgICBzdW1tYXJpc2UoQ291bnQ9bigpLCBwYXN0ZShtb3RpZl9sb2MsIGNvbGxhcHNlPScsICcpKSAlPiUgCiMgICB1bmdyb3VwKCkgJT4lIAojICAgIyBjb2xsYXBzZSB0byBHRlAvUkZQL2lQU0MKIyAgIG11dGF0ZShUeXBlID0gY2FzZV93aGVuKGdyZXBsKCdHRlAnLCBzYW1wbGUpIH4gJ0dGUCcsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgnUkZQJywgc2FtcGxlKSB+ICdSRlAnLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdpUFNDJykpICU+JSAKIyAgIGdyb3VwX2J5KEdlbmUsIFR5cGUpICU+JSAKIyAgIHN1bW1hcmlzZShUb3RhbD1zdW0oQ291bnQpKSAlPiUgCiMgICBhcnJhbmdlKC1Ub3RhbCkgJT4lIAojICAgc3ByZWFkKEdlbmUsIFRvdGFsKSAlPiUgdCgpIAojIAojIGNvbG5hbWVzKGVucmljaGVkX2dlbmVzKSA8LSBlbnJpY2hlZF9nZW5lc1sxLF0KIyBlbnJpY2hlZF9nZW5lcyA8LSBlbnJpY2hlZF9nZW5lc1stMSxdICU+JSBkYXRhLmZyYW1lKCkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbignR2VuZScpICU+JSAgbXV0YXRlKEdGUCA9IGFzLm51bWVyaWMoR0ZQKSwgaVBTQyA9IGFzLm51bWVyaWMoaVBTQyksIFJGUCA9IGFzLm51bWVyaWMoKFJGUCkpKQojIAojIGVucmljaGVkX2dlbmVzW2lzLm5hKGVucmljaGVkX2dlbmVzKV0gPC0gMAojIAojIGVucmljaGVkX2dlbmVzICU+JSBtdXRhdGUoYGRlbHRhR0ZQIDwtPiBSRlBgID0gR0ZQIC0gUkZQKSAlPiUgYXJyYW5nZSgtYGRlbHRhR0ZQIDwtPiBSRlBgLCBHRlApICU+JSBoZWFkKDEwMDApICU+JSBEVDo6ZGF0YXRhYmxlKHJvd25hbWVzID0gRikKCmBgYAoKCmBgYHtyfQojIyBkb2VzIFBBWDYgcmVndWxhdGUgUEFYNj8KI1llcywgeWVzIGl0IGRvZXMuCgojR0ZQIHNwZWNpZmljIQogIAojIGJvdGggJT4lIAojICAgIyBvbmx5IGtlZXAgb25lIGdlbmUgcGVyIG1vdGlmCiMgICBncm91cF9ieShtb3RpZl9sb2MsIHNhbXBsZSwgR2VuZSkgJT4lIAojICAgdG9wX24oMSwgZGlzdGFuY2UpICU+JSAKIyAgIHVuZ3JvdXAoKSAlPiUgCiMgICAjIGtlZXAgdXAgdG8gdHdvIGdlbmVzIHBlciBtb3RpZgojICAgZ3JvdXBfYnkobW90aWZfbG9jLCBzYW1wbGUpICU+JSAKIyAgIHRvcF9uKDIsIGRpc3RhbmNlKSAlPiUgCiMgICB1bmdyb3VwKCkgJT4lIAojICAgIyBhcnJhbmdlIGJ5IGdlbmVzIG1vc3QgbGlua2VkIHRvIG1vdGlmICAKIyAgIGdyb3VwX2J5KEdlbmUsIHNhbXBsZSkgJT4lIAojICAgc3VtbWFyaXNlKENvdW50PW4oKSwgYE1vdGlmIExvY2F0aW9uc2AgPSBwYXN0ZShtb3RpZl9sb2MsIGNvbGxhcHNlPScsICcpKSAlPiUgCiMgICBhcnJhbmdlKC1Db3VudCkgJT4lIAojICAgZmlsdGVyKEdlbmU9PSdQQVg2JykKYGBgCgo=